﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using Aliyun.OSS.NetCore;

namespace CK.Sprite.FileStorage.Application
{
    public class OSSStorageProvider : StorageProvider
    {

        private OssClient client;
        private string bucket;

        public OSSStorageProvider(OSSStorageOptions options) : base(options)
        {
            var endpoint = new Uri(options.ServerAddress);
            client = new OssClient(endpoint, options.AccessKeyId, options.AccessKeySecret);
            bucket = options.Bucket;
        }

        public void GetBucket(string bucketName)
        {
            BucketInfo bucketInfo = client.GetBucketInfo(bucketName);
            if (bucketInfo == null) 
            {
                client.CreateBucket(bucketName);
            }
            var acl = client.GetBucketAcl(bucketName);
            if (acl.ACL == CannedAccessControlList.Private)
            {
                client.SetBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            }
        }

        public override Task<int> DeleteFileAsync(string remoteFilename, string groupName = null)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 下载文件到本地
        /// </summary>
        /// <param name="remoteFilename">远端文件名称</param>
        /// <param name="localFilename">本地文件全路径</param>
        /// <param name="groupName">Bucket名称</param>
        /// <returns></returns>
        public override async Task<int> DownloadFileAsync(string remoteFilename, string localFilename, string groupName = null)
        {
            var response = client.GetObject(groupName, remoteFilename);
            using (var requestStream = response.Content)
            {
                byte[] buf = new byte[1024];
                var fs = Alphaleonis.Win32.Filesystem.File.Open(localFilename, FileMode.OpenOrCreate,FileAccess.ReadWrite);
                var len = 0;
                // 通过输入流将文件的内容读取到文件或者内存中。
                while ((len = requestStream.Read(buf, 0, 1024)) != 0)
                {
                    fs.Write(buf, 0, len);
                }
                fs.Close();
            }
            return 0;

        }

        /// <summary>
        /// 存储文件
        /// </summary>
        /// <param name="fileName">本地文件全路径</param>
        /// <param name="ext">文件后缀</param>
        /// <param name="groupName">Bucket名称</param>
        /// <returns></returns>
        public override async Task<string[]> StoreAsync(string fileName, string ext, string groupName = null)
        {
            GetBucket(groupName);
            var key = Guid.NewGuid().ToString();
            var newFileName = $"{key}.{ext}";
            FileStream streamUpload = Alphaleonis.Win32.Filesystem.File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            var result = client.PutObject(groupName, newFileName, streamUpload);
            return CreateResult(newFileName, groupName);
        }

        public override async Task<string[]> StoreAsync(Stream fileStream, string ext, string groupName = null)
        {
            GetBucket(groupName);
            var key = Guid.NewGuid().ToString();
            var newFileName = $"{key}.{ext}";
            var result = client.PutObject(groupName, newFileName, fileStream);
            return CreateResult(newFileName, groupName);
        }

        /// <summary>
        /// OSS返回全路径因为必须以bucket的三级域名访问
        /// </summary>
        /// <param name="newFileName"></param>
        /// <param name="groupName"></param>
        /// <returns></returns>
        private string[] CreateResult(string newFileName, string groupName)
        {
            return new[]
            {
                string.Join(UrlSeparator, groupName, newFileName),
                string.Join(UrlSeparator, Options.HttpDomain, newFileName)
            };
        }

        public override string CalculateDownServerPath(string serverPath, string fileName)
        {
            if (string.IsNullOrEmpty(serverPath))
            {
                return "";
            }
            try
            {
                string name = Alphaleonis.Win32.Filesystem.Path.GetFileName(fileName);
                var objectName = Alphaleonis.Win32.Filesystem.Path.GetFileName(serverPath);
                var req = new GeneratePresignedUriRequest(bucket, objectName, SignHttpMethod.Get)
                {
                    Expiration = DateTime.Now.AddDays(1)
                };
                req.ResponseHeaders.ContentDisposition = "attachment;filename=" + name;
                var uri = client.GeneratePresignedUri(req);

                return uri.ToString();
            }
            catch (Exception)
            {
                return "";
            }
        }
    }
}

